SecurityContextHolder之策略模式源码分析

您所在的位置:网站首页 inheritablethreadlocal 线程安全 SecurityContextHolder之策略模式源码分析

SecurityContextHolder之策略模式源码分析

2023-09-02 05:22| 来源: 网络整理| 查看: 265

springsecurity 之 登录用户数据的获取

springsecurity中,用户登录信息本质是保存到HttpSession中,springsecurity进行封装 获取登录数据有两种思路:

从SecurityContextHolder中获取从当前请求对象中获取从SecurityContextHolder中获取@RestController public class HelloController { @GetMapping("/hello") public void hello() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); System.out.println("authentication.getClass() = " + authentication.getClass()); } }

SecurityContextHolder存放的是SecurityContext ,SecurityContextHolder中定义三种不同的数据存储策略,采用了策略模式

MODE_THREADLOCAL :将SecurityContext放在ThreadLocal中,开启子线程,子线程获取不到用户数据。MODE_INHERITABLETHREADLOCAL:多线程环境,子线程也能获取到用户数据。MODE_GLOBAL:数据保存到一个静态变量中,web开发中很少使用。

SecurityContextHolderStrategy接口用来规范存储策略中的方法

public interface SecurityContextHolderStrategy { void clearContext(); SecurityContext getContext(); void setContext(SecurityContext var1); SecurityContext createEmptyContext(); }

有三个实现类 对应三个不同的存储策略

ThreadLocalSecurityContextHolderStrategy// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.springframework.security.core.context; import org.springframework.util.Assert; final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy { private static final ThreadLocal contextHolder = new ThreadLocal(); ThreadLocalSecurityContextHolderStrategy() { } public void clearContext() { contextHolder.remove(); } public SecurityContext getContext() { SecurityContext ctx = (SecurityContext)contextHolder.get(); if (ctx == null) { ctx = this.createEmptyContext(); contextHolder.set(ctx); } return ctx; } public void setContext(SecurityContext context) { Assert.notNull(context, "Only non-null SecurityContext instances are permitted"); contextHolder.set(context); } public SecurityContext createEmptyContext() { return new SecurityContextImpl(); } }

存储载体是ThreadLocal 针对SecurityContext的操作都是在ThreadLocal中进行操作。SecurityContext只是个接口,只有一个实现类是SecurityContextImpl

InheritableThreadLocalSecurityContextHolderStrategy// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.springframework.security.core.context; import org.springframework.util.Assert; final class InheritableThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy { private static final ThreadLocal contextHolder = new InheritableThreadLocal(); InheritableThreadLocalSecurityContextHolderStrategy() { } public void clearContext() { contextHolder.remove(); } public SecurityContext getContext() { SecurityContext ctx = (SecurityContext)contextHolder.get(); if (ctx == null) { ctx = this.createEmptyContext(); contextHolder.set(ctx); } return ctx; } public void setContext(SecurityContext context) { Assert.notNull(context, "Only non-null SecurityContext instances are permitted"); contextHolder.set(context); } public SecurityContext createEmptyContext() { return new SecurityContextImpl(); } }

存储载体为InheritableThreadLocal ,InheritableThreadLocal继承ThreadLocal,多了一个特性,就是在子线程创建的时间,会自动将父线程的数据复制到子线程中。实现了子线程中能够获取登录数据的功能。

GlobalSecurityContextHolderStrategy// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.springframework.security.core.context; import org.springframework.util.Assert; final class GlobalSecurityContextHolderStrategy implements SecurityContextHolderStrategy { private static SecurityContext contextHolder; GlobalSecurityContextHolderStrategy() { } public void clearContext() { contextHolder = null; } public SecurityContext getContext() { if (contextHolder == null) { contextHolder = new SecurityContextImpl(); } return contextHolder; } public void setContext(SecurityContext context) { Assert.notNull(context, "Only non-null SecurityContext instances are permitted"); contextHolder = context; } public SecurityContext createEmptyContext() { return new SecurityContextImpl(); } }

存储载体是一个静态变量,也可以在多线程环境下使用,但用的较少。

SecurityContextHolder源码// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.springframework.security.core.context; import java.lang.reflect.Constructor; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; public class SecurityContextHolder { public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL"; public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL"; public static final String MODE_GLOBAL = "MODE_GLOBAL"; public static final String SYSTEM_PROPERTY = "spring.security.strategy"; private static String strategyName = System.getProperty("spring.security.strategy"); private static SecurityContextHolderStrategy strategy; private static int initializeCount = 0; public SecurityContextHolder() { } public static void clearContext() { strategy.clearContext(); } public static SecurityContext getContext() { return strategy.getContext(); } public static int getInitializeCount() { return initializeCount; } private static void initialize() { if (!StringUtils.hasText(strategyName)) { strategyName = "MODE_THREADLOCAL"; } if (strategyName.equals("MODE_THREADLOCAL")) { strategy = new ThreadLocalSecurityContextHolderStrategy(); } else if (strategyName.equals("MODE_INHERITABLETHREADLOCAL")) { strategy = new InheritableThreadLocalSecurityContextHolderStrategy(); } else if (strategyName.equals("MODE_GLOBAL")) { strategy = new GlobalSecurityContextHolderStrategy(); } else { try { Class clazz = Class.forName(strategyName); Constructor customStrategy = clazz.getConstructor(); strategy = (SecurityContextHolderStrategy)customStrategy.newInstance(); } catch (Exception var2) { ReflectionUtils.handleReflectionException(var2); } } ++initializeCount; } public static void setContext(SecurityContext context) { strategy.setContext(context); } public static void setStrategyName(String strategyName) { SecurityContextHolder.strategyName = strategyName; initialize(); } public static SecurityContextHolderStrategy getContextHolderStrategy() { return strategy; } public static SecurityContext createEmptyContext() { return strategy.createEmptyContext(); } public String toString() { return "SecurityContextHolder[strategy='" + strategyName + "'; initializeCount=" + initializeCount + "]"; } static { initialize(); } }

SecurityContextHolder 定义三个静态常量描述三种不同存储策略,在静态代码块中初始化,根据不同的strategyName初始化不同的存储策略,可以调用配置系统变量或者调用setStrategyName改变策略。

在默认情况下,从子线程中获取登录数据是获取不到的,因为默认是MODE_THREADLOCAL策略。

存储策略在System.getProperty("spring.security.strategy");加载,可以配置系统变量来修改策略。

在VM option参数添加

-Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL

SpringContextHolder默认将用户信息存储在ThreadLocal中,在SpringBoot中不同的请求使用不同的线程处理,是怎么获取到用户的信息的呢? 下篇文章揭晓,有懂的可以在评论区留言。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3